#include "OgreAMLPrecompiledHeader.h"

#include "OgreMeshDefinition.h"
#include <OgreMeshManager.h>
#include <OgreException.h>
#include <OgreLogManager.h>
#include <OgreResourceGroupManager.h>
#include <OgreMeshSerializer.h>


namespace Ogre
{

//-------------------------------------------------------------------------------------------------------------------------------------------------
MeshDefinition::MeshDefinition( ResourceManager* creator,
							   const String& name,
							   ResourceHandle handle,
							   const String& group,
							   bool isManual,
							   ManualResourceLoader* loader )
: Resource(creator,name,handle,group,isManual,loader)
, mMeshFileName(name)
, mLodMethod(LM_MANUAL)
, mReductionMethod(ProgressiveMesh::VRQ_CONSTANT)
, mReductionValue(0)
, mPreloadLodMeshes(true)
{
}


//-------------------------------------------------------------------------------------------------------------------------------------------------
MeshDefinition::~MeshDefinition()
{
	// have to call this here reather than in Resource destructor
	// since calling virtual methods in base destructors causes crash
	unload();
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::loadImpl()
{
	// Just create a mesh
	MeshManager::getSingleton().create( mName, mGroup, true, this );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::unloadImpl()
{
	// nothing to do
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
size_t MeshDefinition::calculateSize() const
{
	return sizeof(MeshDefinition) + mLodUsageList.size() * sizeof(LodUsage) + mMeshFileName.size();
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::prepareResource(Resource* resource)
{
	// Is resource type correct?
	if( resource->getCreator()->getResourceType() !=
		MeshManager::getSingleton().getResourceType() )
	{
		OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS,
			"Invalid resource type ' " + resource->getCreator()->getResourceType() + "'"
			" for resource '" + resource->getName() + "'.",
			"MeshDefinition::loadResource" );
	}

	// Load from specified  file
	if( resource->getCreator()->getVerbose() )
	{
		LogManager::getSingleton().logMessage( "Mesh: Loading " + mMeshFileName + "." );
	}

	// Open resource from disk
	mFreshFromDisk = ResourceGroupManager::getSingleton().openResource( mMeshFileName, mGroup, true, resource );
	
	// Fully prebuffer into host RAM
	mFreshFromDisk = DataStreamPtr( OGRE_NEW_T( MemoryDataStream, MEMCATEGORY_GENERAL )
		( mName, mFreshFromDisk ), SPFM_DELETE_T );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::loadResource(Resource* resource)
{
	// Is resource type correct?
	if( resource->getCreator()->getResourceType() !=
		MeshManager::getSingleton().getResourceType() )
	{
		OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS,
			"Invalid resource type ' " + resource->getCreator()->getResourceType() + "'"
			" for resource '" + resource->getName() + "'.",
			"MeshDefinition::loadResource" );
	}

	// Only preload if manual lods are used
	if( mPreloadLodMeshes && mLodMethod == LM_MANUAL && mLodUsageList.empty() )
	{
		OGRE_LOCK_AUTO_MUTEX
		for( LodUsageList::iterator it = mLodUsageList.begin();
			it != mLodUsageList.end(); it++ )
		{
			// Load mesh for manual LOD
			MeshManager::getSingleton().load( it->meshName, mGroup );
		}
	}

	Mesh* mesh = static_cast<Mesh*>( resource );

	// Is data loaded
	if( mFreshFromDisk.isNull() )
	{
		// Prepare resource
		prepareResource( resource );
	}
	
	MeshSerializer serializer;
	serializer.setListener( MeshManager::getSingleton().getListener() );
	
	// If the only copy is local on the stack, it will be cleaned
	// up reliably in case of exceptions, etc
	DataStreamPtr data( mFreshFromDisk );
	mFreshFromDisk.setNull();
	
	if( data.isNull() )
	{
		OGRE_EXCEPT( Exception::ERR_INVALID_STATE,
			"Data doesn't appear to have been prepared in " + mName,
			"MeshDefinition::loadResource()");
	}
	
	serializer.importMesh( data, mesh );
	
	// check all submeshes to see if their materials should be
	// updated.  If the submesh has texture aliases that match those
	// found in the current material then a new material is created using
	// the textures from the submesh.
	mesh->updateMaterialForAllSubMeshes();

	// Check lod method
	if( mLodMethod == LM_MANUAL )
	{
		if( mLodUsageList.empty() )
			return;
		
		// Remove all lods
		mesh->removeLodLevels();

		OGRE_LOCK_AUTO_MUTEX
		for( LodUsageList::iterator it = mLodUsageList.begin();
			it != mLodUsageList.end(); it++ )
		{
			// Create manual mesh level for mesh
			mesh->createManualLodLevel( it->value, it->meshName, mGroup );
		}
	}

	// Is progressive lod setuped?
	if( mProgressiveLodValueList.empty() )
		return;

	// Automatically generates lower level of detail versions of the mesh 
	mesh->generateLodLevels( mProgressiveLodValueList, mReductionMethod, mReductionValue );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setMeshFileName( const String& name )
{
	mMeshFileName = name;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
const String& MeshDefinition::getMeshFileName() const
{
	return mMeshFileName;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::addLodLevel( Real value, const String& meshName )
{
	LodUsage usage;
	usage.value = value;
	usage.meshName = meshName;

	// Add LOD usage to list
	OGRE_LOCK_AUTO_MUTEX
	mLodUsageList.push_back( usage );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::removeLodLevels()
{
	// Remove all items from lod usage list
	OGRE_LOCK_AUTO_MUTEX
	mLodUsageList.clear();
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
MeshDefinition::LodUsageListIterator MeshDefinition::getLodUsageListIterator()
{
	return LodUsageListIterator( mLodUsageList );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
MeshDefinition::LodUsageListConstIterator MeshDefinition::getLodUsageListIterator() const
{
	return LodUsageListConstIterator( mLodUsageList );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setLodMethod( LodMethod method )
{
	mLodMethod = method;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
MeshDefinition::LodMethod MeshDefinition::getLodMethod() const
{
	return mLodMethod;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setProgressiveMeshReductionMethod( ProgressiveMesh::VertexReductionQuota reductionMethod )
{
	mReductionMethod = reductionMethod;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
ProgressiveMesh::VertexReductionQuota MeshDefinition::getProgressiveMeshReductionMethod() const
{
	return mReductionMethod;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setReductionValue( Real value )
{
	mReductionValue = value;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
Real MeshDefinition::getReductionValue() const
{
	return mReductionValue;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setProgressiveLodValueList( const Mesh::LodValueList& lodValues )
{
	mProgressiveLodValueList = lodValues;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
const Mesh::LodValueList& MeshDefinition::getProgressiveLodValueList() const
{
	return mProgressiveLodValueList;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void MeshDefinition::setPreloadLodMeshes( bool value )
{
	mPreloadLodMeshes = value;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
bool MeshDefinition::getIsPreloadLodMeshes() const
{
	return mPreloadLodMeshes;
}

} // end of namespace Ogre
